﻿#include "interpreter.h"

const int num_operator_kind = 6;

void process_brakets(string &statement)
{
	string operator_precedent[num_operator_kind][4] = 
	{
		{"|", "_", "", ""}, 
		{"*", "/", "", ""},
		{"+", "-", "", ""},
		{"=", ":=", "", ""},
		{"<", ">", "<=", ">="},
		{"&&", "||", "==", ""}
	};

	convert_optr(statement); // 연산자 앞에 \ 를 넣어서 구분을 용이

	int kind = num_operator_kind - 1;
	while (kind >= 0)
	{
		string operator_name;
		int i = 0;
		for(i = 0; i < static_cast<int>(statement.length()); i++)
		{
			if(statement[i] == '\\')
			{
				// 문법 오류 처리!
				try
				{ get_operator(statement, i, operator_name);}
				catch(Error::Syntax_error err)
				{
					cout << err.error_message << endl;
					throw Error::General_error(0);
				}

				if(precedent_match(operator_name, kind, operator_precedent))
				{
					int find = i; // 현재 연산자의 \\ 를 가리키고 있다.
					string original_operator = operator_name;

					find = find + original_operator.length() + 1; // 연산자 바로 뒤를 가리킨다.
					statement.insert(find, "(");

					find ++;
					int count = 0;
					while (find < static_cast<int>(statement.length()))
					{
						if(statement[find] == '(')
							count ++;
						else if(statement[find] == ')')
							count --;

						if(statement[find] == ')' && count == -1)
						{
							statement.insert(find + 1, ")");
							break;
						}
						if(statement[find] == '\\' && count == 0)
						{
							try
							{ get_operator(statement, find, operator_name); }
							catch(Error::Syntax_error err)
							{
								cout << err.error_message << endl;
								throw Error::General_error(0);
							}
							// 즉 a + {b + c 와 같은 상황에서 a + {b} + c 로 만든다.
							if(get_rank_operator(operator_name, operator_precedent) >= kind)
							{
								statement.insert(find, ")");
								break;
							}
						}
						if(find == statement.length() - 1)
						{
							statement.insert(find + 1, ")");
							break;
						}

						find ++;
					}

					find = i; 
					count = 0;

					statement.insert(find, ")");
					find --;

					while (find >= 0)
					{
						if(statement[find] == '(')
							count ++;
						else if(statement[find] == ')')
							count --;

						if(statement[find] == '(' && count == 1)
						{
							statement.insert(find + 1, "(");
							break;
						}
						if(statement[find] == '\\' && count == 0)
						{
							try
							{ get_operator(statement, find, operator_name); }
							catch(Error::Syntax_error err)
							{
								cout << err.error_message << endl;
								throw Error::General_error(0);
							}
							// 즉 a + b} + c 와 같은 상황에서 a + {b} + c 로 만든다.
							if(get_rank_operator(operator_name, operator_precedent) >= kind)
							{
								statement.insert(find + operator_name.length() + 1, "(");
								break;
							}
						}
						if(find == 0)
						{
							statement.insert(find, "(");
							break;
						}

						find --;
					}
				}
				i = i + operator_name.length() + 1;
			}
		}
		kind --;
	}

	// 문장 전체를 괄호로 감싼다.
	statement.insert(statement.length(), ")");
	statement.insert(0, "(");

	optimize_braket(statement);
}
void convert_optr(string &str)
{
	for(int i = 0; i < static_cast<int>(str.length()); i++)
	{
		if(str[i] == '=' || str[i] == '<' || str[i] == '>' || str[i] == ':')
		{
			if(str[i + 1] == '=')
			{
				str.insert(i, "\\");
				i += 2;
				continue;
			}
			else
			{
				str.insert(i, "\\");
				i += 1;
				continue;
			}	
		}
		else if(str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/' 
			|| str[i] == '|' || str[i] == '_')
		{
			str.insert(i, "\\");
			i += 1;
			continue;
		}
		else if(str[i] == '&' || str[i] == '|')
		{
			str.insert(i, "\\");
			i += 2;
			continue;
		}
	}
}
bool get_operator(string &str, int location, string &operator_name)
{
	// location 은 \ 를 가리키고 있어야 한다.
	if(str[location] != '\\')
		throw Error::Syntax_error(0);

	if(location == str.length() - 1)
		throw Error::Syntax_error(2);

	if(str[location + 1] == '=' || str[location + 1] == '<' 
		|| str[location + 1] == '>' || str[location + 1] == ':')
	{
		if(location + 2 == str.length())
			throw Error::Syntax_error(2);

		if(str[location + 2] == '=')
		{
			operator_name = str.substr(location + 1, 2);
			return true;
		}
		else
		{
			operator_name = str.substr(location + 1, 1);
			return true;
		}	
	}
	else if(str[location + 1] == '+' || str[location + 1] == '-'
		|| str[location + 1] == '*' || str[location + 1] == '/' 
		|| str[location + 1] == '|' || str[location + 1] == '_')
	{
		operator_name = str.substr(location + 1, 1);
		return true;
	}
	else if(str[location + 1] == '&' || str[location + 1] == '|')
	{
		if(location + 2 == str.length())
			throw Error::Syntax_error(2);
		else if(str[location + 2] !=  str[location + 1])
			throw Error::Syntax_error(3);
		else 
		{
			operator_name = str.substr(location + 1, 2);
			return true;
		}
	}
	return false;
}
bool precedent_match(string opt, int rank, string operator_precedent[][4])
{
	int i = 0;
	while (i < 4)
	{
		if(operator_precedent[rank][i] == opt)
			return true;
		i ++;
	}
	return false;
}
int get_rank_operator(string opt, string operator_precedent[][4])
{
	int i = 0;
	for(int rank = 0; rank < num_operator_kind; rank ++)
	{
		i = 0;
		while (i < 4)
		{
			if(operator_precedent[rank][i] == opt)
				return rank;
			i ++;
		}
	}
	throw Error::Syntax_error(3);
}
void optimize_braket(string &statement)
{
	int location = 0;

	while (location < static_cast<int>(statement.length()) )
	{
		if(statement[location] == '(')
		{
			int find = location;
			bool is_left_braket_alone = false;

			// 현재 find 에는 내가 찾은 괄호의 위치가 저장되어 있으므로
			// 1 을 빼준다. 
			find --;
			while (find >= 0)
			{
				if(statement[find] == '(')
				{
					is_left_braket_alone = true;
					break;
				}
				else if(statement[find] == ' ')
				{
					find --;
					continue;
				}
				else
					break;
			}

			if(is_left_braket_alone)
			{
				// 먼저 오른쪽 괄호를 찾는다.
				int count = 0;
				find = location;

				while (find < static_cast<int>(statement.length()) )
				{
					if(statement[find] == '(')
						count ++;
					else if(statement[find] == ')')
						count --;

					if(count == 0)
						break;

					find ++;
				}

				// 현재 find 에는 내가 찾은 ) 의 위치가 저장되어 있음
				int right_braket_loc = find;

				find ++;
				// 오른쪽 괄호 부터 ) 다음에 바로 ) 가 연속되어 나오는지 확인
				while (find < static_cast<int>(statement.length()) )
				{
					if(statement[find] == ')')
					{
						statement.erase(right_braket_loc, 1);
						statement.erase(location, 1);
						location --; 
						break;
					}
					else if(statement[find] == ' ')
					{
						find ++;
						continue;
					}
					else
						break;
				}
			}
		}
		location ++;
	}
}